
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

/**
 * AUTO-GENERATED FILE. DO NOT MODIFY.
 */

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
import { each, isObject, isArray, createHashMap, map, assert, isString, indexOf, isStringSafe, isNumber } from 'zrender/lib/core/util.js';
import env from 'zrender/lib/core/env.js';
import { isNumeric, getRandomIdBase, getPrecision, round } from './number.js';
import { warn } from './log.js';

function interpolateNumber(p0, p1, percent) {
	return (p1 - p0) * percent + p0;
}
/**
 * Make the name displayable. But we should
 * make sure it is not duplicated with user
 * specified name, so use '\0';
 */

var DUMMY_COMPONENT_NAME_PREFIX = 'series\0';
var INTERNAL_COMPONENT_ID_PREFIX = '\0_ec_\0';
/**
 * If value is not array, then translate it to array.
 * @param  {*} value
 * @return {Array} [value] or value
 */

export function normalizeToArray(value) {
	return value instanceof Array ? value : value == null ? [] : [value];
}
/**
 * Sync default option between normal and emphasis like `position` and `show`
 * In case some one will write code like
 *     label: {
 *          show: false,
 *          position: 'outside',
 *          fontSize: 18
 *     },
 *     emphasis: {
 *          label: { show: true }
 *     }
 */

export function defaultEmphasis(opt, key, subOpts) {
	// Caution: performance sensitive.
	if (opt) {
		opt[key] = opt[key] || {};
		opt.emphasis = opt.emphasis || {};
		opt.emphasis[key] = opt.emphasis[key] || {}; // Default emphasis option from normal

		for (var i = 0, len = subOpts.length; i < len; i++) {
			var subOptName = subOpts[i];

			if (!opt.emphasis[key].hasOwnProperty(subOptName) && opt[key].hasOwnProperty(subOptName)) {
				opt.emphasis[key][subOptName] = opt[key][subOptName];
			}
		}
	}
}
export var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([
//     'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',
//     'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
//     // FIXME: deprecated, check and remove it.
//     'textStyle'
// ]);

/**
 * The method does not ensure performance.
 * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
 * This helper method retrieves value from data.
 */

export function getDataItemValue(dataItem) {
	return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem;
}
/**
 * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
 * This helper method determine if dataItem has extra option besides value
 */

export function isDataItemOption(dataItem) {
	return isObject(dataItem) && !(dataItem instanceof Array); // // markLine data can be array
	// && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
}

/**
 * Mapping to existings for merge.
 *
 * Mode "normalMege":
 *     The mapping result (merge result) will keep the order of the existing
 *     component, rather than the order of new option. Because we should ensure
 *     some specified index reference (like xAxisIndex) keep work.
 *     And in most cases, "merge option" is used to update partial option but not
 *     be expected to change the order.
 *
 * Mode "replaceMege":
 *     (1) Only the id mapped components will be merged.
 *     (2) Other existing components (except internal components) will be removed.
 *     (3) Other new options will be used to create new component.
 *     (4) The index of the existing components will not be modified.
 *     That means their might be "hole" after the removal.
 *     The new components are created first at those available index.
 *
 * Mode "replaceAll":
 *     This mode try to support that reproduce an echarts instance from another
 *     echarts instance (via `getOption`) in some simple cases.
 *     In this scenario, the `result` index are exactly the consistent with the `newCmptOptions`,
 *     which ensures the component index referring (like `xAxisIndex: ?`) corrent. That is,
 *     the "hole" in `newCmptOptions` will also be kept.
 *     On the contrary, other modes try best to eliminate holes.
 *     PENDING: This is an experimental mode yet.
 *
 * @return See the comment of <MappingResult>.
 */

export function mappingToExists(existings, newCmptOptions, mode) {
	var isNormalMergeMode = mode === 'normalMerge';
	var isReplaceMergeMode = mode === 'replaceMerge';
	var isReplaceAllMode = mode === 'replaceAll';
	existings = existings || [];
	newCmptOptions = (newCmptOptions || []).slice();
	var existingIdIdxMap = createHashMap(); // Validate id and name on user input option.

	each(newCmptOptions, function (cmptOption, index) {
		if (!isObject(cmptOption)) {
			newCmptOptions[index] = null;
			return;
		}

		if (process.env.NODE_ENV !== 'production') {
			// There is some legacy case that name is set as `false`.
			// But should work normally rather than throw error.
			if (cmptOption.id != null && !isValidIdOrName(cmptOption.id)) {
				warnInvalidateIdOrName(cmptOption.id);
			}

			if (cmptOption.name != null && !isValidIdOrName(cmptOption.name)) {
				warnInvalidateIdOrName(cmptOption.name);
			}
		}
	});
	var result = prepareResult(existings, existingIdIdxMap, mode);

	if (isNormalMergeMode || isReplaceMergeMode) {
		mappingById(result, existings, existingIdIdxMap, newCmptOptions);
	}

	if (isNormalMergeMode) {
		mappingByName(result, newCmptOptions);
	}

	if (isNormalMergeMode || isReplaceMergeMode) {
		mappingByIndex(result, newCmptOptions, isReplaceMergeMode);
	} else if (isReplaceAllMode) {
		mappingInReplaceAllMode(result, newCmptOptions);
	}

	makeIdAndName(result); // The array `result` MUST NOT contain elided items, otherwise the
	// forEach will omit those items and result in incorrect result.

	return result;
}

function prepareResult(existings, existingIdIdxMap, mode) {
	var result = [];

	if (mode === 'replaceAll') {
		return result;
	} // Do not use native `map` to in case that the array `existings`
	// contains elided items, which will be omitted.

	for (var index = 0; index < existings.length; index++) {
		var existing = existings[index]; // Because of replaceMerge, `existing` may be null/undefined.

		if (existing && existing.id != null) {
			existingIdIdxMap.set(existing.id, index);
		} // For non-internal-componnets:
		//     Mode "normalMerge": all existings kept.
		//     Mode "replaceMerge": all existing removed unless mapped by id.
		// For internal-components:
		//     go with "replaceMerge" approach in both mode.

		result.push({
			existing: mode === 'replaceMerge' || isComponentIdInternal(existing) ? null : existing,
			newOption: null,
			keyInfo: null,
			brandNew: null
		});
	}

	return result;
}

function mappingById(result, existings, existingIdIdxMap, newCmptOptions) {
	// Mapping by id if specified.
	each(newCmptOptions, function (cmptOption, index) {
		if (!cmptOption || cmptOption.id == null) {
			return;
		}

		var optionId = makeComparableKey(cmptOption.id);
		var existingIdx = existingIdIdxMap.get(optionId);

		if (existingIdx != null) {
			var resultItem = result[existingIdx];
			assert(!resultItem.newOption, 'Duplicated option on id "' + optionId + '".');
			resultItem.newOption = cmptOption; // In both mode, if id matched, new option will be merged to
			// the existings rather than creating new component model.

			resultItem.existing = existings[existingIdx];
			newCmptOptions[index] = null;
		}
	});
}

function mappingByName(result, newCmptOptions) {
	// Mapping by name if specified.
	each(newCmptOptions, function (cmptOption, index) {
		if (!cmptOption || cmptOption.name == null) {
			return;
		}

		for (var i = 0; i < result.length; i++) {
			var existing = result[i].existing;

			if (!result[i].newOption // Consider name: two map to one.
      // Can not match when both ids existing but different.
      && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) {
				result[i].newOption = cmptOption;
				newCmptOptions[index] = null;
				return;
			}
		}
	});
}

function mappingByIndex(result, newCmptOptions, brandNew) {
	each(newCmptOptions, function (cmptOption) {
		if (!cmptOption) {
			return;
		} // Find the first place that not mapped by id and not internal component (consider the "hole").

		var resultItem;
		var nextIdx = 0;

		while ( // Be `!resultItem` only when `nextIdx >= result.length`.
			(resultItem = result[nextIdx]) && ( // (1) Existing models that already have id should be able to mapped to. Because
			// after mapping performed, model will always be assigned with an id if user not given.
			// After that all models have id.
			// (2) If new option has id, it can only set to a hole or append to the last. It should
			// not be merged to the existings with different id. Because id should not be overwritten.
			// (3) Name can be overwritten, because axis use name as 'show label text'.
				resultItem.newOption || isComponentIdInternal(resultItem.existing) || // In mode "replaceMerge", here no not-mapped-non-internal-existing.
    resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) {
			nextIdx++;
		}

		if (resultItem) {
			resultItem.newOption = cmptOption;
			resultItem.brandNew = brandNew;
		} else {
			result.push({
				newOption: cmptOption,
				brandNew: brandNew,
				existing: null,
				keyInfo: null
			});
		}

		nextIdx++;
	});
}

function mappingInReplaceAllMode(result, newCmptOptions) {
	each(newCmptOptions, function (cmptOption) {
		// The feature "reproduce" requires "hole" will also reproduced
		// in case that component index referring are broken.
		result.push({
			newOption: cmptOption,
			brandNew: true,
			existing: null,
			keyInfo: null
		});
	});
}
/**
 * Make id and name for mapping result (result of mappingToExists)
 * into `keyInfo` field.
 */

function makeIdAndName(mapResult) {
	// We use this id to hash component models and view instances
	// in echarts. id can be specified by user, or auto generated.
	// The id generation rule ensures new view instance are able
	// to mapped to old instance when setOption are called in
	// no-merge mode. So we generate model id by name and plus
	// type in view id.
	// name can be duplicated among components, which is convenient
	// to specify multi components (like series) by one name.
	// Ensure that each id is distinct.
	var idMap = createHashMap();
	each(mapResult, function (item) {
		var existing = item.existing;
		existing && idMap.set(existing.id, item);
	});
	each(mapResult, function (item) {
		var opt = item.newOption; // Force ensure id not duplicated.

		assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id));
		opt && opt.id != null && idMap.set(opt.id, item);
		!item.keyInfo && (item.keyInfo = {});
	}); // Make name and id.

	each(mapResult, function (item, index) {
		var existing = item.existing;
		var opt = item.newOption;
		var keyInfo = item.keyInfo;

		if (!isObject(opt)) {
			return;
		} // Name can be overwritten. Consider case: axis.name = '20km'.
		// But id generated by name will not be changed, which affect
		// only in that case: setOption with 'not merge mode' and view
		// instance will be recreated, which can be accepted.

		keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name // Avoid that different series has the same name,
		// because name may be used like in color pallet.
			: DUMMY_COMPONENT_NAME_PREFIX + index;

		if (existing) {
			keyInfo.id = makeComparableKey(existing.id);
		} else if (opt.id != null) {
			keyInfo.id = makeComparableKey(opt.id);
		} else {
			// Consider this situatoin:
			//  optionA: [{name: 'a'}, {name: 'a'}, {..}]
			//  optionB [{..}, {name: 'a'}, {name: 'a'}]
			// Series with the same name between optionA and optionB
			// should be mapped.
			var idNum = 0;

			do {
				keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++;
			} while (idMap.get(keyInfo.id));
		}

		idMap.set(keyInfo.id, item);
	});
}

function keyExistAndEqual(attr, obj1, obj2) {
	var key1 = convertOptionIdName(obj1[attr], null);
	var key2 = convertOptionIdName(obj2[attr], null); // See `MappingExistingItem`. `id` and `name` trade string equals to number.

	return key1 != null && key2 != null && key1 === key2;
}
/**
 * @return return null if not exist.
 */

function makeComparableKey(val) {
	if (process.env.NODE_ENV !== 'production') {
		if (val == null) {
			throw new Error();
		}
	}

	return convertOptionIdName(val, '');
}

export function convertOptionIdName(idOrName, defaultValue) {
	if (idOrName == null) {
		return defaultValue;
	}

	return isString(idOrName) ? idOrName : isNumber(idOrName) || isStringSafe(idOrName) ? idOrName + '' : defaultValue;
}

function warnInvalidateIdOrName(idOrName) {
	if (process.env.NODE_ENV !== 'production') {
		warn('`' + idOrName + '` is invalid id or name. Must be a string or number.');
	}
}

function isValidIdOrName(idOrName) {
	return isStringSafe(idOrName) || isNumeric(idOrName);
}

export function isNameSpecified(componentModel) {
	var name = componentModel.name; // Is specified when `indexOf` get -1 or > 0.

	return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));
}
/**
 * @public
 * @param {Object} cmptOption
 * @return {boolean}
 */

export function isComponentIdInternal(cmptOption) {
	return cmptOption && cmptOption.id != null && makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0;
}
export function makeInternalComponentId(idSuffix) {
	return INTERNAL_COMPONENT_ID_PREFIX + idSuffix;
}
export function setComponentTypeToKeyInfo(mappingResult, mainType, componentModelCtor) {
	// Set mainType and complete subType.
	each(mappingResult, function (item) {
		var newOption = item.newOption;

		if (isObject(newOption)) {
			item.keyInfo.mainType = mainType;
			item.keyInfo.subType = determineSubType(mainType, newOption, item.existing, componentModelCtor);
		}
	});
}

function determineSubType(mainType, newCmptOption, existComponent, componentModelCtor) {
	var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType // Use determineSubType only when there is no existComponent.
		: componentModelCtor.determineSubType(mainType, newCmptOption); // tooltip, markline, markpoint may always has no subType

	return subType;
}
/**
 * A helper for removing duplicate items between batchA and batchB,
 * and in themselves, and categorize by series.
 *
 * @param batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
 * @param batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
 * @return result: [resultBatchA, resultBatchB]
 */

export function compressBatches(batchA, batchB) {
	var mapA = {};
	var mapB = {};
	makeMap(batchA || [], mapA);
	makeMap(batchB || [], mapB, mapA);
	return [mapToArray(mapA), mapToArray(mapB)];

	function makeMap(sourceBatch, map, otherMap) {
		for (var i = 0, len = sourceBatch.length; i < len; i++) {
			var seriesId = convertOptionIdName(sourceBatch[i].seriesId, null);

			if (seriesId == null) {
				return;
			}

			var dataIndices = normalizeToArray(sourceBatch[i].dataIndex);
			var otherDataIndices = otherMap && otherMap[seriesId];

			for (var j = 0, lenj = dataIndices.length; j < lenj; j++) {
				var dataIndex = dataIndices[j];

				if (otherDataIndices && otherDataIndices[dataIndex]) {
					otherDataIndices[dataIndex] = null;
				} else {
					(map[seriesId] || (map[seriesId] = {}))[dataIndex] = 1;
				}
			}
		}
	}

	function mapToArray(map, isData) {
		var result = [];

		for (var i in map) {
			if (map.hasOwnProperty(i) && map[i] != null) {
				if (isData) {
					result.push(+i);
				} else {
					var dataIndices = mapToArray(map[i], true);
					dataIndices.length && result.push({
						seriesId: i,
						dataIndex: dataIndices
					});
				}
			}
		}

		return result;
	}
}
/**
 * @param payload Contains dataIndex (means rawIndex) / dataIndexInside / name
 *                         each of which can be Array or primary type.
 * @return dataIndex If not found, return undefined/null.
 */

export function queryDataIndex(data, payload) {
	if (payload.dataIndexInside != null) {
		return payload.dataIndexInside;
	} else if (payload.dataIndex != null) {
		return isArray(payload.dataIndex) ? map(payload.dataIndex, function (value) {
			return data.indexOfRawIndex(value);
		}) : data.indexOfRawIndex(payload.dataIndex);
	} else if (payload.name != null) {
		return isArray(payload.name) ? map(payload.name, function (value) {
			return data.indexOfName(value);
		}) : data.indexOfName(payload.name);
	}
}
/**
 * Enable property storage to any host object.
 * Notice: Serialization is not supported.
 *
 * For example:
 * let inner = zrUitl.makeInner();
 *
 * function some1(hostObj) {
 *      inner(hostObj).someProperty = 1212;
 *      ...
 * }
 * function some2() {
 *      let fields = inner(this);
 *      fields.someProperty1 = 1212;
 *      fields.someProperty2 = 'xx';
 *      ...
 * }
 *
 * @return {Function}
 */

export function makeInner() {
	var key = '__ec_inner_' + innerUniqueIndex++;
	return function (hostObj) {
		return hostObj[key] || (hostObj[key] = {});
	};
}
var innerUniqueIndex = getRandomIdBase();
/**
 * The same behavior as `component.getReferringComponents`.
 */

export function parseFinder(ecModel, finderInput, opt) {
	var _a = preParseFinder(finderInput, opt),
		mainTypeSpecified = _a.mainTypeSpecified,
		queryOptionMap = _a.queryOptionMap,
		others = _a.others;

	var result = others;
	var defaultMainType = opt ? opt.defaultMainType : null;

	if (!mainTypeSpecified && defaultMainType) {
		queryOptionMap.set(defaultMainType, {});
	}

	queryOptionMap.each(function (queryOption, mainType) {
		var queryResult = queryReferringComponents(ecModel, mainType, queryOption, {
			useDefault: defaultMainType === mainType,
			enableAll: opt && opt.enableAll != null ? opt.enableAll : true,
			enableNone: opt && opt.enableNone != null ? opt.enableNone : true
		});
		result[mainType + 'Models'] = queryResult.models;
		result[mainType + 'Model'] = queryResult.models[0];
	});
	return result;
}
export function preParseFinder(finderInput, opt) {
	var finder;

	if (isString(finderInput)) {
		var obj = {};
		obj[finderInput + 'Index'] = 0;
		finder = obj;
	} else {
		finder = finderInput;
	}

	var queryOptionMap = createHashMap();
	var others = {};
	var mainTypeSpecified = false;
	each(finder, function (value, key) {
		// Exclude 'dataIndex' and other illgal keys.
		if (key === 'dataIndex' || key === 'dataIndexInside') {
			others[key] = value;
			return;
		}

		var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || [];
		var mainType = parsedKey[1];
		var queryType = (parsedKey[2] || '').toLowerCase();

		if (!mainType || !queryType || opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) {
			return;
		}

		mainTypeSpecified = mainTypeSpecified || !!mainType;
		var queryOption = queryOptionMap.get(mainType) || queryOptionMap.set(mainType, {});
		queryOption[queryType] = value;
	});
	return {
		mainTypeSpecified: mainTypeSpecified,
		queryOptionMap: queryOptionMap,
		others: others
	};
}
export var SINGLE_REFERRING = {
	useDefault: true,
	enableAll: false,
	enableNone: false
};
export var MULTIPLE_REFERRING = {
	useDefault: false,
	enableAll: true,
	enableNone: true
};
export function queryReferringComponents(ecModel, mainType, userOption, opt) {
	opt = opt || SINGLE_REFERRING;
	var indexOption = userOption.index;
	var idOption = userOption.id;
	var nameOption = userOption.name;
	var result = {
		models: null,
		specified: indexOption != null || idOption != null || nameOption != null
	};

	if (!result.specified) {
		// Use the first as default if `useDefault`.
		var firstCmpt = void 0;
		result.models = opt.useDefault && (firstCmpt = ecModel.getComponent(mainType)) ? [firstCmpt] : [];
		return result;
	}

	if (indexOption === 'none' || indexOption === false) {
		assert(opt.enableNone, '`"none"` or `false` is not a valid value on index option.');
		result.models = [];
		return result;
	} // `queryComponents` will return all components if
	// both all of index/id/name are null/undefined.

	if (indexOption === 'all') {
		assert(opt.enableAll, '`"all"` is not a valid value on index option.');
		indexOption = idOption = nameOption = null;
	}

	result.models = ecModel.queryComponents({
		mainType: mainType,
		index: indexOption,
		id: idOption,
		name: nameOption
	});
	return result;
}
export function setAttribute(dom, key, value) {
	dom.setAttribute ? dom.setAttribute(key, value) : dom[key] = value;
}
export function getAttribute(dom, key) {
	return dom.getAttribute ? dom.getAttribute(key) : dom[key];
}
export function getTooltipRenderMode(renderModeOption) {
	if (renderModeOption === 'auto') {
		// Using html when `document` exists, use richText otherwise
		return env.domSupported ? 'html' : 'richText';
	} else {
		return renderModeOption || 'html';
	}
}
/**
 * Group a list by key.
 */

export function groupData(array, getKey // return key
) {
	var buckets = createHashMap();
	var keys = [];
	each(array, function (item) {
		var key = getKey(item);
		(buckets.get(key) || (keys.push(key), buckets.set(key, []))).push(item);
	});
	return {
		keys: keys,
		buckets: buckets
	};
}
/**
 * Interpolate raw values of a series with percent
 *
 * @param data         data
 * @param labelModel   label model of the text element
 * @param sourceValue  start value. May be null/undefined when init.
 * @param targetValue  end value
 * @param percent      0~1 percentage; 0 uses start value while 1 uses end value
 * @return             interpolated values
 *                     If `sourceValue` and `targetValue` are `number`, return `number`.
 *                     If `sourceValue` and `targetValue` are `string`, return `string`.
 *                     If `sourceValue` and `targetValue` are `(string | number)[]`, return `(string | number)[]`.
 *                     Other cases do not supported.
 */

export function interpolateRawValues(data, precision, sourceValue, targetValue, percent) {
	var isAutoPrecision = precision == null || precision === 'auto';

	if (targetValue == null) {
		return targetValue;
	}

	if (isNumber(targetValue)) {
		var value = interpolateNumber(sourceValue || 0, targetValue, percent);
		return round(value, isAutoPrecision ? Math.max(getPrecision(sourceValue || 0), getPrecision(targetValue)) : precision);
	} else if (isString(targetValue)) {
		return percent < 1 ? sourceValue : targetValue;
	} else {
		var interpolated = [];
		var leftArr = sourceValue;
		var rightArr = targetValue;
		var length_1 = Math.max(leftArr ? leftArr.length : 0, rightArr.length);

		for (var i = 0; i < length_1; ++i) {
			var info = data.getDimensionInfo(i); // Don't interpolate ordinal dims

			if (info && info.type === 'ordinal') {
				// In init, there is no `sourceValue`, but should better not to get undefined result.
				interpolated[i] = (percent < 1 && leftArr ? leftArr : rightArr)[i];
			} else {
				var leftVal = leftArr && leftArr[i] ? leftArr[i] : 0;
				var rightVal = rightArr[i];
				var value = interpolateNumber(leftVal, rightVal, percent);
				interpolated[i] = round(value, isAutoPrecision ? Math.max(getPrecision(leftVal), getPrecision(rightVal)) : precision);
			}
		}

		return interpolated;
	}
}